﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;
using MethodWorx.CMS.Content;
using System.IO;
using MethodWorx.CMS.UI.Application.ActionResults;
using Microsoft.Win32;
using Ionic.Zip;
using MethodWorx.CMS.Plugins.ActionResults;
using MethodWorx.CMS.Providers;
using MethodWorx.Core.DomainModel;
using MethodWorx.Core;

namespace MethodWorx.CMS.Plugins.AssetManagement
{
    public class AssetFolderManagement : Controller
    {
        private PluginService service;
        private IdeExtension extension;
        private ICmsDataProvider provider;
        private IUnitOfWorkFactory uowFactory;

        /// <summary>
        /// Required constructor, must have an "extension" argument
        /// and any other arguments the dependency injection contain knows about
        /// </summary>
        /// <param name="extension"></param>
        /// <param name="service"></param>
        public AssetFolderManagement(ICmsDataProvider provider, IdeExtension extension, PluginService service, IUnitOfWorkFactory uowFactory) 
        {
            this.service = service;
            this.extension = extension;
            this.provider = provider;
            this.uowFactory = uowFactory;
        }

        /// <summary>
        /// Invoke method, default action for the get
        /// </summary>
        /// <param name="instance"></param>
        /// <returns></returns>
        [HttpGet]
        public ActionResult Add(Guid? assetFolderId)
        {
            this.ViewData["assetFolderId"] = assetFolderId;
            return new EmbeddedViewResult(this, this.extension.WebEditor, null);
        }

        [HttpPost]
        public ActionResult Add(Guid? assetFolderId, string name, bool templateAssets, string submit)
        {
            this.ViewData["assetFolderId"] = assetFolderId;

            ActionResult result = new CloseDialogResult();
            var assetManagementService = new AssetManagementService(this.provider);
            if (submit == "save")
            {
                if (string.IsNullOrEmpty(name))
                    this.ModelState.AddModelError("Name", "Please enter a folder name");
                if (this.ModelState.IsValid)
                {

                    assetManagementService.CreateAssetFolder(assetFolderId, name, templateAssets);

                    //
                    //  tell the UI to refresh the tree
                    result = new RefreshTreeNodeResult().And(new CloseDialogResult()).And(new RefreshTreeNodeResult());
                }
                else
                    result = new EmbeddedViewResult(this, this.extension.WebEditor, null); 
            }

            return result;
        }

        [HttpPost]
        public ActionResult MoveAssets(Guid instance, Guid[] selected, Guid folderId)
        {
            AssetManagementService assetService = new AssetManagementService(this.provider);

            assetService.MoveAssets(instance, selected, folderId);

            return Json(new { success = true  });
        }

        private AssetFolder GetRootAssetFolder()
        {
            AssetManagementService assetService = new AssetManagementService(this.provider);
            return assetService.GetRootAssetFolder();
        }

        private AssetFolder GetAssetFolder(Guid id)
        {
            AssetManagementService assetService = new AssetManagementService(this.provider);
            return assetService.GetAssetFolder(id);
        }

      
    

        [HttpGet]
        public ActionResult Manage(Guid instance, string filter)
        {

            var assetFolder = this.GetAssetFolder(instance);
            //
            //  ok, we have our asset folder
            //
            //  ok, with the assets assembly them into the preview windows
            //  easy peasy
            var assets = this.service.GetAllUserData<Asset>(this.extension.Plugin, assetFolder, "Asset");
            this.ViewData["filters"] = assets.Select(a => Path.GetExtension(a.UserData.Name ?? "").ToUpper()).Distinct().ToArray();
            this.ViewData["filter"] = filter;

            if (!string.IsNullOrEmpty(filter))
            {
                assets = assets.Where(a => Path.GetExtension(a.UserData.Name ?? "").ToUpper() == filter);
            }
            this.ViewData["assets"] = assets;
            this.ViewData["instance"] = instance;
            return new EmbeddedViewResult(this, new AssemblyResourceReference(this.GetType().Assembly, "Plugins/Views/AssetManagement/Manage.ascx"), null);
        }

        public ActionResult SmallPreview(Guid instance, Guid id)
        {
            var assetFolder = this.GetAssetFolder(instance);
            var asset = this.service.GetAllUserData<Asset>(this.extension.Plugin, assetFolder, "Asset").FirstOrDefault(a => a.Id == id);
            if (asset != null)
            {
                if (asset.UserData.MimeType.Contains("text"))
                {
                    //
                    //  output the image not found
                    using (var s = this.GetType().Assembly.GetManifestResourceStream("MethodWorx.CMS.Plugins.AssetManagement.document.png"))
                    {
                        return File(s, "image/png");
                    }
                }
                else
                    return File(asset.UserData.Data, asset.UserData.MimeType);
            }
            return null;
        }

        [HttpGet]
        public ActionResult AddAsset(Guid instance, bool? fromZip)
        {
            var assetFolder = this.GetAssetFolder(instance);
            this.ViewData["fromZip"] = fromZip ?? false;
            this.ViewData["instance"] = instance;

            return new EmbeddedViewResult(this,
                new AssemblyResourceReference(
                    this.GetType().Assembly,
                    "Plugins/Views/AssetManagement/AddAsset.ascx"), null);
        }

        [HttpGet]
        public ActionResult AddAssetPreview(Guid instance, int index)
        {
            var assetFolder = this.GetAssetFolder(instance);
            this.ViewData["instance"] = instance;

            this.ViewData["fromZip"] = true;
            this.ViewData["index"] = index;
            this.ViewData["mimeType"] = this.Session["mimeType." + index];
            this.ViewData["fileName"] = this.Session["fileName." + index];
            this.ViewData["fileSize"] = ((byte[])this.Session["assetPreview." + index]).Length;
            this.ViewData["count"] = this.Session["count"];
            this.ViewData["index"] = index;
            this.ViewData["preview"] = true;

            return new EmbeddedViewResult(this,
                       new AssemblyResourceReference(
                           this.GetType().Assembly,
                           "Plugins/Views/AssetManagement/AddAsset.ascx"), null);
        }

        [HttpGet]
        public ActionResult DeleteAsset(Guid instance, Guid id)
        {
            using (var unitOfWork = this.uowFactory.Begin())
            {
                var assetFolder = this.GetAssetFolder(instance);
                this.ViewData["instance"] = instance;

                service.DeleteUserData(id);

                var map = service.GetUserData<AssetMap>(this.extension.Plugin, assetFolder, "AssetMap");
                if (map != null)
                {
                    //
                    //  remove the key of the asset from the asset key map
                    foreach (string key in map.Map.Keys)
                    {
                        if (map.Map[key] == id)
                        {
                            map.Map.Remove(key);
                            break;
                        }
                    }
                    this.service.SetUserData(this.extension.Plugin, assetFolder, "AssetMap", map, true);
                }
                unitOfWork.Commit();

                return Json(new { success = true }, JsonRequestBehavior.AllowGet);
            }
        }

        [HttpPost]
        public ActionResult AddAsset(Guid instance, string submit, bool? fromZip, bool? fromTree)
        {
            var assetFolder = this.GetAssetFolder(instance);
            this.ViewData["fromZip"] = fromZip ?? false;
            this.ViewData["instance"] = instance;

            if (submit.ToLower() == "upload")
            {
                if (fromZip ?? false)
                    this.UploadAssetFromZip();
                else
                    this.UploadAsset();
            }
            else if (submit.ToLower() == "cancel")
            {
                return new CloseDialogResult();
            }
            else if (submit.ToLower() == "save")
            {
                if (!(fromZip ?? false))
                {
                    this.CreateAsset(
                        instance,
                        (string)this.Session["fileName"],
                        (string)this.Session["mimeType"],
                        (byte[])this.Session["assetPreview"]);
                }
                else
                {
                    for (int i = 0; i < (int)this.Session["count"]; i++)
                    {
                        this.CreateAsset(
                            instance,
                            (string)this.Session["fileName." + i],
                            (string)this.Session["mimeType." + i],
                            (byte[])this.Session["assetPreview." + i]);
                    }
                }


                if((fromTree ?? false) == true)
                {
                    return new CloseDialogResult().And(new RefreshTreeNodeResult());
                }
                else
                {
                    return new RefreshPluginResult(this.extension.Id)
                                .And(new CloseDialogResult()).And(new RefreshTreeNodeResult());
                }
            }

            return new EmbeddedViewResult(this,
            new AssemblyResourceReference(
                this.GetType().Assembly,
                "Plugins/Views/AssetManagement/AddAsset.ascx"), null);
        }

        [HttpGet]
        public ActionResult EditAsset(Guid instance, Guid id)
        {
            this.ViewData["instance"] = instance;
            var assetFolder = this.GetAssetFolder(instance);
            var asset = this.service.GetAllUserData<Asset>(this.extension.Plugin, assetFolder, "Asset").FirstOrDefault(a => a.Id == id);
            if (asset != null)
            {
                if (asset.UserData.MimeType.Contains("text"))
                {
                    this.ViewData["id"] = id;
                    return new EmbeddedViewResult(this, new AssemblyResourceReference(this.GetType().Assembly, "Plugins/Views/AssetManagement/EditAsset.ascx"), asset.UserData);
                }
                else
                    return File(asset.UserData.Data, asset.UserData.MimeType);
            }
            return null;
        }

        [HttpPost]
        public JsonResult SaveAsset(Guid  instance, Guid id, string content)
        {
            var assetFolder = this.GetAssetFolder(instance);
            var asset = this.service.GetAllUserData<Asset>(this.extension.Plugin, assetFolder, "Asset").FirstOrDefault(a => a.Id == id);
            if (asset != null)
            {
                if (asset.UserData.MimeType.Contains("text"))
                {
                    //  set the user data
                    asset.UserData.Data = System.Text.ASCIIEncoding.ASCII.GetBytes(content);
                    this.service.SetUserData(id, asset.UserData);
                }

            }
            return Json(new { success = true }); ;
        }

        [HttpGet]
        public JsonResult RenameFolder(Guid instance, string newName)
        {
            AssetManagementService service = new AssetManagementService(this.provider);
            service.RenameAssetFolder(instance, newName);

            return null;
        }

        [HttpGet]
        public JsonResult GetCss(Guid instance, Guid id)
        {
            var assetFolder = this.GetAssetFolder(instance);
            var asset = this.service.GetAllUserData<Asset>(this.extension.Plugin, assetFolder, "Asset").FirstOrDefault(a => a.Id == id);
            if (asset != null)
            {
                if (asset.UserData.MimeType.Contains("text"))
                {
                    //  set the user data
                    return Json(new { success = true, Css = System.Text.ASCIIEncoding.ASCII.GetString(asset.UserData.Data) }, JsonRequestBehavior.AllowGet);
                }

            }
            return Json(new { success = true, Css = "" }, JsonRequestBehavior.AllowGet);
        }

        private void CreateAsset(Guid instance, string name, string mimeType, byte[] data)
        {
            var assetFolder = this.GetAssetFolder(instance);

            Asset asset = new Asset();
            asset.Name = name;
            asset.MimeType = mimeType;
            asset.Data = data;

            //  and save
            var pud = this.service.SetUserData(
                this.extension.Plugin,
                assetFolder,
                "Asset",
                asset,
                false);

            var map = this.service.GetUserData<AssetMap>(this.extension.Plugin, assetFolder, "AssetMap");
            if (map == null)
            {
                map = new AssetMap();
                map.Map = new Dictionary<string, Guid>();
            }
            if (map.Map.ContainsKey(asset.Name.ToLower()))
                map.Map[asset.Name.ToLower()] = pud.Id;
            else
                map.Map.Add(asset.Name.ToLower(), pud.Id);
            this.service.SetUserData(this.extension.Plugin, assetFolder, "AssetMap", map, true);
        }

        private void UploadAsset()
        {

            var file = this.Request.Files["file"];
            if (file == null || string.IsNullOrEmpty(file.FileName) || file.InputStream == null)
                this.ModelState.AddModelError("file", "Please choose a valid file to upload");
            else
            {
                //
                //  get the stream
                byte[] buffer = new byte[file.InputStream.Length];
                file.InputStream.Read(buffer, 0, (int)file.InputStream.Length);

                this.Session["assetPreview"] = buffer;
                this.Session["mimeType"] = this.GetMimeType(Path.GetExtension(file.FileName));
                this.Session["fileName"] = file.FileName;


                this.ViewData["preview"] = true;
                this.ViewData["mimeType"] = this.GetMimeType(Path.GetExtension(file.FileName));
                this.ViewData["fileName"] = file.FileName;
                this.ViewData["fileSize"] = file.ContentLength;
            }
        }

        private void UploadAssetFromZip()
        {
            var file = this.Request.Files["file"];
            if (file == null)
                this.ModelState.AddModelError("file", "Please choose a valid file to upload");
            else
            {
                try
                {
                    int i = 0;
                    byte[] buffer = new byte[file.InputStream.Length];
                    file.InputStream.Read(buffer, 0, (int)file.InputStream.Length);
                    using (var zip = ZipFile.Read(buffer))
                    {
                        foreach (var entry in zip.Entries)
                        {
                            using (MemoryStream ms = new MemoryStream())
                            {
                                entry.Extract(ms);

                                ms.Seek(0, SeekOrigin.Begin);
                                byte[] zipBuffer = new byte[ms.Length];
                                ms.Read(zipBuffer, 0, zipBuffer.Length);

                                this.Session["assetPreview." + i] = zipBuffer;
                                this.Session["mimeType." + i] = this.GetMimeType(Path.GetExtension(entry.FileName));
                                this.Session["fileName." + i] = entry.FileName;



                                if (i == 0)
                                {
                                    this.ViewData["mimeType"] = this.GetMimeType(Path.GetExtension(entry.FileName));
                                    this.ViewData["fileName"] = entry.FileName;
                                    this.ViewData["fileSize"] = zipBuffer.Length;
                                }
                                i++;
                            }
                        }
                    }
                    this.Session["count"] = i;
                    this.ViewData["count"] = i;
                    this.ViewData["index"] = 0;
                    this.ViewData["preview"] = true;
                }
                catch (Exception ex)
                {
                    this.ModelState.AddModelError("file", "Could not read zip file, it's contents maybe invalid");
                }


            }
        }

        [HttpGet]
        public ActionResult Preview(int? index)
        {
            if (index.HasValue)
                return File(((byte[])this.Session["assetPreview." + index.ToString()] ?? new byte[] { }), (string)this.Session["mimeType." + index.ToString()]);
            else
                return File(((byte[])this.Session["assetPreview"] ?? new byte[] { }), (string)this.Session["mimeType"]);
        }

        string GetMimeType(string extension)
        {
            return MimeTypes.GetMimeTypeFromExtension(extension, "application/unknown");
        }
    }
}
